package com.wm.blog_admin.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wm.blog_admin.controller.req.IconImportReq;
import com.wm.blog_admin.controller.resp.IconExportResp;
import com.wm.blog_admin.controller.resp.ImportExcelFailResp;
import com.wm.blog_admin.dao.TAdminDAO;
import com.wm.blog_admin.dao.TIconDAO;
import com.wm.blog_admin.dao.TMenuDAO;
import com.wm.blog_admin.query.TIconQuery;
import com.wm.blog_admin.task.IconThreadTask;
import com.wm.blog_common.base.BeanUtils;
import com.wm.blog_common.bean.PageHelper;
import com.wm.blog_common.bean.QueryPage;
import com.wm.blog_common.constatnt.CommonConstant;
import com.wm.blog_common.constatnt.ErrorConstant;
import com.wm.blog_common.domain.TIconDO;
import com.wm.blog_common.entity.TAdmin;
import com.wm.blog_common.entity.TIcon;
import com.wm.blog_common.entity.TMenu;
import com.wm.blog_common.enums.DeleteFlag;
import com.wm.blog_common.exception.BusinessException;
import com.wm.blog_common.result.Result;
import com.wm.blog_common.util.CommonUtil;
import com.wm.blog_common.util.DateUtil;
import com.wm.blog_common.util.ObjectValidateUtil;
import com.wm.blog_common.util.excel.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author wm_yu
 * @since 2020-04-15
 */
@Service
@Slf4j
public class TIconService{
    @Autowired
    private TIconDAO tIconDAO;

    @Autowired
    private TAdminDAO tAdminDAO;

    @Autowired
    private TMenuDAO tMenuDAO;


    @Autowired
    private ThreadPoolTaskExecutor poolTaskExecutor;


    /**
     * 根据ID查询
     * @param id
     * @return
     */
    public Result<TIconDO> selectById(Long id){
        return Result.success(BeanUtils.copyProperties(tIconDAO.getById(id),TIconDO.class));
    }

    /**
     * 根据ID删除
     * @param id
     * @return
     */
    public Result deleteById(Long id){
        return tIconDAO.removeById(id)?Result.success():Result.failure();
    }

    /**
     * 添加
     * @param tIcon
     * @return
     */
    public Result insert(TIcon tIcon){
        return tIconDAO.save(tIcon)?Result.success():Result.failure();
    }

    /**
     * 修改
     * @param tIcon
     * @return
     */
    public Result update(TIcon tIcon){
        return tIconDAO.updateById(tIcon)?Result.success():Result.failure();
    }

    /**
     * 统计总数
     *
     * @param query
     * @return
     */
    public Result<Integer> count(TIconQuery query) {
        return Result.success(tIconDAO.count(this.combineQuery(query)));
    }

    /**
     * 列表查询
     * @param query
     * @return
     */
    public Result<List<TIconDO>> list(TIconQuery query){
        return Result.success(BeanUtils.copyListProperties(tIconDAO.list(this.combineQuery(query)),TIconDO.class));
    }
    /**
     * 分页查询
     * @param page
     * @return
     */
    public Result<Page<TIconDO>> page(QueryPage page, TIconQuery query){
        query.setOrderSql("sort");
        return Result.success(PageHelper.newPage(tIconDAO.page(page,this.combineQuery(query)),TIconDO.class));
    }

    /**
     * 组装查询条件
     *
     * @param query
     * @return
     */
    private QueryWrapper<TIcon> combineQuery(TIconQuery query) {
        query = Optional.ofNullable(query).orElse(new TIconQuery());
        QueryWrapper<TIcon> wrapper = new QueryWrapper<>();
        final boolean isDesc = query.getIsDesc();
        Optional.ofNullable(query.getStatus()).ifPresent(x -> {
            wrapper.eq("satus",x);
        });
        Optional.ofNullable(query.getOrderSql()).filter(StringUtils::isNotEmpty).ifPresent(x -> {
            if (isDesc){
                wrapper.orderByDesc(x);
            }else{
                wrapper.orderByAsc(x);
            }
        });
        return wrapper;
    }


    /**
     * 批量导入 返回失败原因
     * @param file
     * @return
     */
    public Result uploadExcel(MultipartFile file, Long adminId) {
        //查询当前登陆人姓名
        String userName = Optional.ofNullable(tAdminDAO.getById(adminId)).orElse(new TAdmin()).getUserName();
        List<ImportExcelFailResp> failRespList = new ArrayList<>();
        try(InputStream inputStream = file.getInputStream()) {
            List<String> headerList = CommonUtil.getExcelHeaderList(IconImportReq.class);
            List<IconImportReq> reqList = ExcelUtil.ReadExcelToObject(inputStream, IconImportReq.class, headerList, ExcelUtil.excel_enum.getSuffix(file.getContentType()), null,3);
            //校验不合要求的集合
            if(!CollectionUtils.isEmpty(reqList)){
                //记录icon名称,不重复
                List<String> nameList = new ArrayList<>();
                List<IconImportReq> failList = reqList
                        .stream()
                        .filter(x -> ObjectUtils.isEmpty(x) || !ObjectValidateUtil.validate(x).isSuccess())
                        .collect(Collectors.toList());
                failRespList.addAll(failList
                        .stream()
                        .map(x -> {
                            ImportExcelFailResp resp = new ImportExcelFailResp();
                            String msg = ObjectValidateUtil.validate(x).getMsg();
                            resp.setFailReason(msg);
                            resp.setRowNum(x.getExcelNum());
                            return resp;
                        })
                        .collect(Collectors.toList()));
                reqList.removeAll(failList);
                //行号排序
                List<IconImportReq> sortList = reqList
                        .stream()
                        .sorted(Comparator.comparing(IconImportReq::getExcelNum))
                        .collect(Collectors.toList());
                Iterator<IconImportReq> iterator = sortList.iterator();
                //剔除重名的数据
                while(iterator.hasNext()){
                    IconImportReq importReq = iterator.next();
                    String type = importReq.getType();
                    if(nameList.contains(type)){
                        //加入错误原因list
                        this.addFailList(importReq.getExcelNum(),ErrorConstant.ICON_TYPE_REPERT,failRespList);
                        iterator.remove();
                    }else{
                        nameList.add(type);
                    }
                }
                //剔除和已经数据库中重名的数据
                List<String> varList = reqList
                        .stream()
                        .map(IconImportReq::getType)
                        .collect(Collectors.toList());

                QueryWrapper<TIcon> wrapper = new QueryWrapper<>();
                wrapper.in("type",varList);
                List<String> baseNameList = Optional
                        .ofNullable(tIconDAO
                                .list(wrapper))
                        .orElse(Collections.emptyList())
                        .stream()
                        .map(TIcon::getType)
                        .collect(Collectors.toList());
                iterator = sortList.iterator();
                while(iterator.hasNext()){
                    IconImportReq importReq = iterator.next();
                    String type = importReq.getType();
                    if(baseNameList.contains(type)){
                        //加入错误原因list
                        this.addFailList(importReq.getExcelNum(), ErrorConstant.ICON_TYPE_REPERT_DATA,failRespList);
                        iterator.remove();
                    }
                }
                if(!CollectionUtils.isEmpty(sortList)){
                    //查询当前最大的排序,追加
                    Integer maxSort = this.getMaxSort();
                    //构建导入数据的list
                    Date now = DateUtil.getCurrentDate();
                    List<TIcon> iconList = sortList
                            .stream()
                            .map(x -> {
                                TIcon icon = new TIcon();
                                BeanUtils.copyProperties(x, icon);
                                icon.setSatus(DeleteFlag.ENABLE.getValue());
                                icon.setEditor(userName);
                                icon.setCreator(userName);
                                icon.setCreateTime(now);
                                icon.setUpdateTime(now);
                                icon.setSort(maxSort + 1);
                                return icon;
                            })
                            .collect(Collectors.toList());
                    //导入数据
                    boolean b = tIconDAO.saveBatch(iconList);
                    return b ? Result.success(failRespList):Result.failure();
                }
            }
        }catch (Exception e){
            log.error("批量导入获取文件流异常:{}",e.getMessage(),e);
        }
        return Result.success(failRespList);
    }


    private void addFailList(Integer excelNum,String failReason,List list){
        ImportExcelFailResp failResp = new ImportExcelFailResp();
        failResp.setRowNum(excelNum);
        failResp.setFailReason(failReason);
        list.add(failResp);
    }

    /**
     * 删除icon图标
     * @param id
     * @return
     */
    public Result deleteIcon(Long id) {
        Optional.ofNullable(id).orElseThrow(() -> new BusinessException(ErrorConstant.ICON_ID_empty));
        //校验图标是否在使用中,在使用中不允许删除图标
        TIcon icon = Optional.ofNullable(tIconDAO.getById(id)).orElse(new TIcon());
        Optional.ofNullable(icon.getId()).orElseThrow(() -> new BusinessException(ErrorConstant.ICON_NOT_EXSIT));
        QueryWrapper<TMenu> wrapper = new QueryWrapper<>();
        wrapper.eq("type",icon.getType());
        wrapper.last("limit 1");
        List<TMenu> menuList = Optional.ofNullable(tMenuDAO.list(wrapper)).orElse(Collections.emptyList());
        if(!CollectionUtils.isEmpty(menuList)){
            return Result.failure(ErrorConstant.ICON_IS_USE);
        }
        return tIconDAO.removeById(id) ? Result.success():Result.failure();
    }


    /**
     * 用户下载icon上传模板
     */
    public void downTemplate(HttpServletResponse response) {
        ClassPathResource classPathResource = new ClassPathResource("templates/菜单icon上传模板.xlsx");
        try(InputStream inputStream =classPathResource.getInputStream()) {
            String title = "icon批量上传模板";
            ExcelUtil.downTemplate(inputStream,response,title);
        }catch (Exception e){
            log.error("下载模板文件失败",e);
        }
    }


    /**
     *查询iconList,未被禁用
     * @return
     */
    public Result iconList() {
        TIconQuery query = new TIconQuery();
        query.setStatus(DeleteFlag.ENABLE.getValue());
        QueryWrapper<TIcon> wrapper = this.combineQuery(query);
        List<TIcon> iconList = Optional.ofNullable(tIconDAO.list(wrapper)).orElse(Collections.emptyList());
        return Result.success(iconList);
    }

    /**
     * 图标详情查询
     * @param id
     * @return
     */
    public Result getById(Long id) {
        Optional.ofNullable(id).orElseThrow(() -> new BusinessException(ErrorConstant.ICON_ID_empty));
        return Result.success(Optional.ofNullable(tIconDAO.getById(id)).orElse(new TIcon()));
    }


    /**
     * 新增/编辑icon
     * @param query
     * @return
     */
    public Result saveOrUpdate(TIconQuery query) {
        Optional.ofNullable(query.getType()).orElseThrow(() -> new BusinessException(ErrorConstant.ICON_TYPE_EMPTY));
        //登录人
        String name = Optional
                .ofNullable(tAdminDAO
                        .getById(query.getAdminId()))
                .orElse(new TAdmin())
                .getUserName();
        Date now = DateUtil.getCurrentDate();
        TIcon icon = new TIcon();
        BeanUtils.copyProperties(query,icon);
        icon.setUpdateTime(now);
        if(!ObjectUtils.isEmpty(query.getId())){
            //修改
            icon.setEditor(name);
            return tIconDAO.updateById(icon) ? Result.success():Result.failure();
        }else{
            //新增
            icon.setCreator(name);
            icon.setCreateTime(now);
            //追加排序到最后
            icon.setSort(this.getMaxSort() + 1);
            return tIconDAO.save(icon) ? Result.success():Result.failure();
        }
    }

    /**
     * icon置顶操作
     * @param query
     * @return
     */
    public Result topIcon(TIconQuery query) {
        Optional.ofNullable(query.getId()).orElseThrow(() -> new BusinessException(ErrorConstant.ICON_ID_empty));
        //查询当前icon
        TIcon icon = Optional.ofNullable(tIconDAO.getById(query.getId())).orElse(new TIcon());
        //查询当前排序最前的icon
        QueryWrapper<TIcon> wrapper = this.combineQuery(new TIconQuery());
        wrapper.last("order by sort asc limit 1 ");
        TIcon sortIcon = Optional.ofNullable(tIconDAO.getOne(wrapper)).orElse(new TIcon());
        if(sortIcon.getId().equals(icon.getId())){
            return Result.success(ErrorConstant.ICON_SORT_TOP);
        }else{
            //交换两者的排序
            int sort = icon.getSort();
            icon.setSort(sortIcon.getSort());
            sortIcon.setSort(sort);
            List<TIcon> iconList = new ArrayList<TIcon>(){{
                add(icon);
                add(sortIcon);
            }};
            return tIconDAO.updateBatchById(iconList) ? Result.success():Result.failure();
        }
    }


    private Integer getMaxSort(){
        //追加排序到最后
        TIconQuery iconQuery = new TIconQuery();
        iconQuery.setOrderSql("sort");
        iconQuery.setIsDesc(true);
        QueryWrapper<TIcon> wrapper = this.combineQuery(iconQuery);
        wrapper.last("limit 1");
        TIcon tIcon = Optional.ofNullable(tIconDAO.getOne(wrapper)).orElse(new TIcon());
        return ObjectUtils.isEmpty(tIcon.getId()) ? 0:tIcon.getSort();
    }

    /**
     * excel导出
     * @param query
     * @param response
     */
    public void export(TIconQuery query, HttpServletResponse response) {
        //使用多线程查询
        Integer current = 1;
        Integer size = 1;
        QueryPage page = new QueryPage(current, size);
        Result<Page<TIconDO>> result = this.page(page, query);
        if(result.isSuccess()){
            Long total = result.getData().getTotal();
            if(total > 0){
                //计算需要的线程数量
                Integer threadCount = CommonUtil.getThreadCount(total.intValue());
                List<Future<List<IconExportResp>>> futureList = new ArrayList<>();
                for (Integer i = 1; i <= threadCount; i++) {
                    //构建多线程请求入参
                    Future<List<IconExportResp>> future = poolTaskExecutor.submit(new IconThreadTask(i, CommonConstant.DEFAULT_MAX_SIZE, this, query));
                    futureList.add(future);
                }
                //处理线程结果
                if(!CollectionUtils.isEmpty(futureList)){
                    List<IconExportResp> respList = new ArrayList<>();
                    for (Future<List<IconExportResp>> future : futureList) {
                        try {
                            respList.addAll(future.get());
                        } catch (InterruptedException | ExecutionException e) {
                            log.error("线程获取数据失败:[{}]",e.getMessage(),e);
                            //取消当前任务
                            future.cancel(true);
                        }
                    }
                    //导出excel
                    List<String> headerList = CommonUtil.getExcelHeaderList(IconExportResp.class);
                    String title = CommonConstant.ICON_EXPORT_TITLE;
                    ExcelUtil.downloadExcelFile(title,headerList,respList,response);
                }
            }
        }
    }
}







